package vn.mahjonggame.game;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Appender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;

import vn.mahjonggame.game.event.OnUserGoneHandler;
import vn.mahjonggame.game.event.OnUserJoinHandler;
import vn.mahjonggame.game.request.CallChiHandler;
import vn.mahjonggame.game.request.CallFinishHandler;
import vn.mahjonggame.game.request.CallKanHandler;
import vn.mahjonggame.game.request.CallPonHandler;
import vn.mahjonggame.game.request.CallRiichiHandler;
import vn.mahjonggame.game.request.CallRonHandler;
import vn.mahjonggame.game.request.CallTsumoHandler;
import vn.mahjonggame.game.request.CancelKanHandler;
import vn.mahjonggame.game.request.CancelPonHandler;
import vn.mahjonggame.game.request.CancelRonHandler;
import vn.mahjonggame.game.request.PickTileHandler;
import vn.mahjonggame.game.request.PickTileOnDeathWallHandler;
import vn.mahjonggame.game.request.ReadyHandler;
import vn.mahjonggame.game.request.SkipHandler;
import vn.mahjonggame.game.request.StartHandler;
import vn.mahjonggame.game.request.ThrowTileAfterKanHandler;
import vn.mahjonggame.game.request.ThrowTileHandler;
import vn.mahjonggame.game.request.UpdateRoomHandler;
import vn.mahjonggame.metadata.*;
import vn.mahjonggame.platform.PlatformClient;
import vn.mahjonggame.platform.PlatformConfig;
import vn.mahjonggame.platform.ServerMode;
import vn.mahjonggame.util.Common;

import com.smartfoxserver.v2.core.SFSEventType;
import com.smartfoxserver.v2.entities.Room;
import com.smartfoxserver.v2.entities.User;
import com.smartfoxserver.v2.entities.data.SFSArray;
import com.smartfoxserver.v2.entities.data.SFSObject;
import com.smartfoxserver.v2.entities.variables.RoomVariable;
import com.smartfoxserver.v2.entities.variables.SFSRoomVariable;
import com.smartfoxserver.v2.entities.variables.SFSUserVariable;
import com.smartfoxserver.v2.entities.variables.UserVariable;
import com.smartfoxserver.v2.extensions.ExtensionLogLevel;
import com.smartfoxserver.v2.extensions.SFSExtension;

public class MahjongExtension extends SFSExtension {	
	private List<User> players = null;	
	private User owner = null;
	private List<GameAction> gameActions = new ArrayList<GameAction>();	
	private int playerMode = PlayerMode.TUTORIAL_MODE.ordinal();	
	private MatchState matchState = MatchState.WAIT_MORE_PLAYERS;	
	private long lastUpdateState = 0;
	private List<Integer> emptyPosition = null;		
	private int turn;
	private Mahjong mahjong = null;
	private Logger logger = null;
	private PlatformClient client = null;
	
	@Override
	public void init()
	{					   
		addRequestHandler(GameActionType.ready.toString(), ReadyHandler.class);
	    addRequestHandler(GameActionType.start.toString(), StartHandler.class);
	    
		addRequestHandler(GameActionType.pickTile.toString(), PickTileHandler.class);
		addRequestHandler(GameActionType.pickTileFromDeathWall.toString(), PickTileOnDeathWallHandler.class);
		addRequestHandler(GameActionType.throwTile.toString(), ThrowTileHandler.class);
		addRequestHandler(GameActionType.throwTileAfterKan.toString(), ThrowTileAfterKanHandler.class);
		
		addRequestHandler(GameActionType.callChi.toString(), CallChiHandler.class);
		addRequestHandler(GameActionType.callPon.toString(), CallPonHandler.class);
		addRequestHandler(GameActionType.cancelPon.toString(), CancelPonHandler.class);
		
		addRequestHandler(GameActionType.callKan.toString(), CallKanHandler.class);
		addRequestHandler(GameActionType.cancelKan.toString(), CancelKanHandler.class);				
		addRequestHandler(GameActionType.callRiichi.toString(), CallRiichiHandler.class);
		
		addRequestHandler(GameActionType.skip.toString(), SkipHandler.class);		
		
		addRequestHandler(GameActionType.cancelRon.toString(), CancelRonHandler.class);
		
		addRequestHandler(GameActionType.callTsumo.toString(), CallTsumoHandler.class);
		addRequestHandler(GameActionType.callRon.toString(), CallRonHandler.class);
		addRequestHandler(GameActionType.callFinish.toString(), CallFinishHandler.class);
		addRequestHandler(GameActionType.updateRoom.toString(), UpdateRoomHandler.class);
		
		addEventHandler(SFSEventType.USER_JOIN_ROOM, OnUserJoinHandler.class);
		
	    addEventHandler(SFSEventType.USER_DISCONNECT, OnUserGoneHandler.class);
	    addEventHandler(SFSEventType.USER_LEAVE_ROOM, OnUserGoneHandler.class);
	    
	    players = getParentRoom().getUserList();
	    owner = getParentRoom().getOwner();	    			
				
		emptyPosition = new ArrayList<Integer>();
		
		for(int i = 0; i < Mahjong.NUM_OF_PLAYER; i++){
			this.getEmptyPosition().add(i);
		}
		
		initLog();		
		mahjong = new Mahjong();
		mahjong.setRoom(getParentRoom());
		mahjong.setLogger(logger);
		mahjong.setIsDebug(getParentRoom().getVariable(PlatformConfig.SERVER_MODE).getStringValue().equalsIgnoreCase(ServerMode.DEBUG.toString()) ? true : false);		
        playerMode = getParentRoom().getVariable(RoomVarName.playMode.toString()).getIntValue();
        
        addAction(getParentRoom().getOwner(),GameActionType.create.toString());               
	}
			
	public void addAction(User user, String action){
		addAction(user,action,"","");
	}
	
	public void addAction(User user, String action, String param1, String param2){
		String actor = user.getName();
		String actor_displayname = user.getVariable(UserVarName.displayname.toString()).getStringValue();
		this.getGameActions().add(new GameAction(actor, actor_displayname, action, param1, param2));
	}
	
	public void removeRoom(){				
		
		/*
		 * Write logs.
		 */
		SFSArray actions = new SFSArray();
				
		for(Iterator<GameAction> iGameActions = gameActions.iterator(); iGameActions.hasNext();){			
			GameAction iGameAction = (GameAction)iGameActions.next();			
			actions.addSFSObject(iGameAction.toSFSObject());
		}
		
		logger.info("MahjongExtension | actions: " + actions.toJson());
		
		if (this.getPlayerMode() == PlayerMode.REAL_MODE.ordinal()){
			String room_id = getParentRoom().getVariable(RoomVarName.room_id.toString()).getStringValue(); 
			String access_token = getParentRoom().getVariable(RoomVarName.access_token.toString()).getStringValue();
			
			SFSObject response = PlatformClient.logs_write(access_token, room_id, actions);
			
			if(!Common.isApiSuccess(response)){
				logger.info("MahjongExtension: Failed in logs_write API.");
			}
		}
		
		for(Iterator<User> iUsers = getParentRoom().getUserList().iterator(); iUsers.hasNext();)
		{
			User iUser = iUsers.next();
			
			logger.info("MahjongExtension | Removed user: " + iUser.getName());			
			getApi().leaveRoom(iUser, getParentRoom(), false, true);
		}
		
		getApi().removeRoom(getParentRoom(), false, true);
	}
		
	public void changeState(MatchState state) 
	{
		this.setLastUpdateState(System.currentTimeMillis());
		
		if (this.getMatchState() != state){
			this.setMatchState(state);
			
	        RoomVariable newState = new SFSRoomVariable(RoomVarName.roomState.toString(), state.ordinal());
	        
	        getApi().setRoomVariables(null, getParentRoom(), Arrays.asList(newState));	        
		}
	}
			
	public void addUserToMatch(User user){		
				
		players.add(user);
		Player newPlayer = null;
		Collections.sort(emptyPosition);
		int position = emptyPosition.remove(0);
		
		if(position == 0 && (user.getId() == this.getOwner().getId())){
			newPlayer = new Player(user.getId(), position, mahjong);
		}
		else {
			newPlayer = new Player(user.getId(), position, mahjong);
		}

		newPlayer.setName(user.getName());		
		newPlayer.setFinalScore(Double.valueOf(getParentRoom().getVariable(RoomVarName.init_score.toString()).getStringValue()));
		newPlayer.setBonusTime(Integer.valueOf(getParentRoom().getVariable(RoomVarName.init_bonus_time.toString()).getStringValue()));
		newPlayer.setTurnTime(Integer.valueOf(getParentRoom().getVariable(RoomVarName.turn_time.toString()).getStringValue()));
		newPlayer.setLogger(logger);
		
		mahjong.getPlayers().add(newPlayer);
		
		UserVariable newState = new SFSUserVariable(UserVarName.status.toString(), PlayerState.JOIN.toString());
		getApi().setUserVariables(user, Arrays.asList(newState));
		
		logger.info(GameActionType.join.toString() + ": New Player " + newPlayer.toString());		
		logger.info(GameActionType.join.toString() + ": Available Position " + emptyPosition);
	}
	
	public boolean isAllPlayerReady()
	{
		for(Iterator<User> iPlayers = this.getPlayers().iterator(); iPlayers.hasNext();){
			
			User iPlayer = iPlayers.next();
			if (iPlayer.getVariable(UserVarName.status.toString()).getStringValue().equalsIgnoreCase(PlayerState.JOIN.toString())) {
				return false;
			}
		}
		return true;
	}
	
	public void setPlayerReady(User user) {
		UserVariable newState = new SFSUserVariable(UserVarName.status.toString(), PlayerState.READY.toString());	    
	    getApi().setUserVariables(user, Arrays.asList(newState));
	    
	    this.getMahjong().getPlayerById(user.getId()).setPlayerState(PlayerState.READY.toString());
	}
		
	public void dealTile(){
		/*
		 * Deal tile
		 */			
		mahjong.dealTile();
		
		while(mahjong.isBadMahjong()){
			mahjong.dealTile();
		}
		
		this.changeState(MatchState.PLAYING);
		
		/*
		 * Lock the room
		 */		
		getParentRoom().setMaxUsers(getParentRoom().getUserList().size());
	}
		
	public List<GameAction> getGameActions() {
		return this.gameActions;
	}

	public void setGameActions(List<GameAction> gameActions) {
		this.gameActions = gameActions;
	}
	
	public MatchState getMatchState() {
		return this.matchState;
	}

	public void setMatchState(MatchState matchState) {
		this.matchState = matchState;
	}
	
	public int getPlayerMode() {
		return this.playerMode;
	}

	public void setPlayerMode(int playerMode) {
		this.playerMode = playerMode;
	}
	
	public long getLastUpdateState() {
		return this.lastUpdateState;
	}
	
	public void setLastUpdateState(long lastUpdateState) {
		this.lastUpdateState = lastUpdateState;
	}
	
	public int getTurn() {
		return this.turn;
	}

	public void setTurn(int turn) {
		this.turn = turn;
	}

	public List<User> getPlayers() {
		return this.players;
	}

	public void setPlayers(List<User> players) {
		this.players = players;
	}
		
	public Mahjong getMahjong() {
		return this.mahjong;
	}
	
	public User getOwner(){
		return this.owner;
	}
	
	public void setOwner(User owner){
		this.owner = owner;
	}
	
	public Room getGameRoom(){
		return getParentRoom();
	}

	public List<Integer> getEmptyPosition() {
		return emptyPosition;
	}

	public void setEmptyPosition(List<Integer> emptyPosition) {
		this.emptyPosition = emptyPosition;
	}
	
	@Override
	public void destroy() 
	{
		super.destroy();
		logger.info("MahjongExtension : Mahjong game was destroyed!");
	}
	
	public void initLog(){
		
		FileAppender fa = new FileAppender();		
		fa.setLayout(new PatternLayout("%d{yyyy MM dd | HH:mm:ss.SSS} | %m%n"));
		fa.setThreshold(Level.DEBUG);
		fa.setAppend(true);		
		
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss.SSS");		
		String logName = "logs/mahjong/" + getGameRoom().getId() + "_" + getGameRoom().getName() + "_" + sdf.format(Calendar.getInstance().getTime()) + ".log";
		
		logger = Logger.getLogger(logName);
		logger.addAppender(fa);
		
		fa.setFile(logName);
		fa.activateOptions();
		
		logger.info(GameActionType.create.toString() + ": Created another log file " + logName);
	}

	public PlatformClient getClient() {
		return client;
	}

	public void setClient(PlatformClient client) {
		this.client = client;
	}
	
}
